#ifndef xpack_math_index_system
#define xpack_math_index_system
#pragma push_macro("xuser")
#undef  xuser
#define xuser mixc::math_index_system::inc
#include"define/base_type.hpp"
#include"macro/xexport.hpp"
#include"macro/xindex_rollback.hpp"
#include"macro/xinterface.hpp"
#include"macro/xstruct.hpp"
#include"macro/xunlikely.hpp"
#include"math/abs.hpp"
#include"meta/is_number.hpp"
#include"meta/origin_of.hpp"
#pragma pop_macro("xuser")

namespace mixc::math_index_system::origin{
    xinterface(
        xname(iinterval),
        xfunc(normalize, bstate_t(uxx length)),
        xattr(left,  uxx()),
        xattr(right, uxx())
    ) $

    template<class object_t>
    concept can_interval = requires(object_t object){
        iinterval(object);
    };

    // bend close iinterval
    xstruct(
        xname(cc),
        xprof(m_left,    ixx),
        xprof(m_right,   ixx)
    )
    public:
        xprops()
            xpubget_prosetx(left, uxx){
                xr{ return uxx(xthe.m_left); }
                xw{ xthe.m_left = ixx(value); }
            };

            xpubget_prosetx(right, uxx){
                xr{ return uxx(xthe.m_right); }
                xw{ xthe.m_right = ixx(value); }
            };

            xpubgetx(length, uxx){
                xr{ 
                    return
                        xthe.m_left <= xthe.m_right ? 
                        xthe.m_right + 1 - xthe.m_left :
                        xthe.m_left + 1 - xthe.m_right;
                }
            };
        $

        xprops_operator()

    public:
        // clang 可能存在 bug，需要设置成 inc::is_number 类型，而不是是 class 类型
        template<class left_t, class right_t = ixx>
        requires(
            inc::is_number<inc::origin_of<left_t, with_propcast>> and
            inc::is_number<inc::origin_of<right_t, with_propcast>>
        )
        cc(left_t && left, right_t && right = right_t(-1)) : 
            m_left(ixx(left)), m_right(ixx(right)){}

        bstate_t normalize(uxx length) const {
            return this->normalize(length, [](){ return bstate_t::success; });
        }

    protected:
        bstate_t normalize(uxx length, auto const & modify) const {
            xunlikely(length == 0){
                return bstate_t::fail;
            }

            xindex_rollback(length, m_left,  +1);
            xindex_rollback(length, m_right, +1);
            
            xunlikely(modify() == bstate_t::fail){
                return bstate_t::fail;
            }

            xunlikely(uxx(m_left) >= length or uxx(m_right) >= length){
                return bstate_t::fail;
            }
            else{
                return bstate_t::success;
            }
        }
    $

    // left close right open iinterval
    struct co : cc{
        using cc::cc;

        bstate_t normalize(uxx length) const {
            return cc::normalize(length, [&](){
                xunlikely(inc::abs(m_left - m_right) == 0){
                    return bstate_t::fail;
                }

                m_right -= m_left <= m_right ? 1 : -1;
                return bstate_t::success;
            });
        }
    };

    // left open right close iinterval
    struct oc : cc{
        using cc::cc;

        bstate_t normalize(uxx length) const {
            return cc::normalize(length, [&](){
                xunlikely(inc::abs(m_left - m_right) == 0){
                    return bstate_t::fail;
                }

                m_left += m_left <= m_right ? 1 : -1;
                return bstate_t::success;
            });
        }
    };

    // bend open iinterval
    struct oo : cc{
        using cc::cc;

        bstate_t normalize(uxx length) const {
            return cc::normalize(length, [&](){
                xunlikely(inc::abs(m_left - m_right) < 2){
                    return bstate_t::fail;
                }

                auto asc = m_left <= m_right;
                m_left  += asc ? 1 : -1;
                m_right -= asc ? 1 : -1;
                return bstate_t::success;
            });
        }
    };
}

namespace mixc{
    using namespace mixc::math_index_system::origin;
}
using namespace mixc::math_index_system::origin;

#endif

xexport_space(mixc::math_index_system::origin)